#include "CAnnouncementsPanel.h"
#include "CGamePanel.h"
#include "CPlayerInfoPanel.h"
#include "CGame.h"

#include "CPlayer.h"

#define MAXWEIGHT (float)((Attributes["Strength"]*2.5)+(Attributes["Endurance"]*5))

CPlayer::CPlayer(std::string name) : CEntity(name), Quit(false)
{
	Level = 0;
}

CPlayer::~CPlayer()
{
}

bool CPlayer::readyToQuit()
{
	return Quit;
}

std::string CPlayer::look(pair position)
{
	std::string here = "Here: ";
	if (gGame->getDungeon()->getFloor(CurrentFloor)->getFovMap()->isInFov(position.x(), position.y()))
	{
		CDungeonTile* tile = gGame->getDungeon()->getFloor(CurrentFloor)->getTile(pair(position.x(), position.y()));
		here += tile->getName();

		if (tile->entities.size() > 0)
		{
			here += ", ";

			for (unsigned int i = 0; i < tile->entities.size(); ++i)
			{
				if (i < tile->entities.size()-1)
				{
					here += tile->entities[i]->getName();
					here += ", ";
				}
				else
					here += tile->entities[i]->getName();
			}
		}

		if (tile->items.size() > 0)
		{
			here += ", ";

			for (unsigned int i = 0; i < tile->items.size(); ++i)
			{
				if (i < tile->items.size()-1)
				{
					here += tile->items[i]->getName();
					here += ", ";
				}
				else
					here += tile->items[i]->getName();
			}
		}

		if (pair(position.x(), position.y()) == Position)
		{
			here += ", ";
			here += Name;
		}

		here += ".";
	}
	else
		here = "Here: Darkness.";

	return here;
}

void CPlayer::takeTurn()
{
	if ((int)CTurnSchedule::getTicks()-TicksSinceCombat >= getSpeed()*4 && Health < MaxHealth)
	{
		TicksSinceCombat = CTurnSchedule::getTicks();
		++Health;
	}

	if (!Dead)
	{
		TCOD_key_t key = TCODConsole::waitForKeypress(true);

		int delay = 0;
		if (key.c == 'q')
		{
			delay = 1;
			Quit = true;
		}
		else if (key.vk == TCODK_UP || key.vk == TCODK_KP8)
			delay = MoveAttack(pair(0, -1));
		else if (key.vk == TCODK_DOWN || key.vk == TCODK_KP2)
			delay = MoveAttack(pair(0, 1));
		else if (key.vk == TCODK_LEFT || key.vk == TCODK_KP4)
			delay = MoveAttack(pair(-1, 0));
		else if (key.vk == TCODK_RIGHT || key.vk == TCODK_KP6)
			delay = MoveAttack(pair(1, 0));
		else if (key.vk == TCODK_KP7)
			delay = MoveAttack(pair(-1, -1));
		else if (key.vk == TCODK_KP9)
			delay = MoveAttack(pair(1, -1));
		else if (key.vk == TCODK_KP1)
			delay = MoveAttack(pair(-1, 1));
		else if (key.vk == TCODK_KP3)
			delay = MoveAttack(pair(1, 1));
		else if (key.c == '.')
			delay = getSpeed();
		else if (key.c == 'W')
		{
			TCODConsole* panel = new TCODConsole(80, 50);
			bool done = false;
			while (!done)
			{
				panel->clear();
				panel->print(5, 4, "Choose an item to remove.");
				ShowWorn(panel);

				TCOD_key_t subkey = TCODConsole::waitForKeypress(false);
				if (subkey.vk == TCODK_ESCAPE)
					done = true;
				else
				{
					if (Inventory[subkey.c])
					{
						if (!EquipSlots[Inventory[subkey.c]->getEquipSlot()].Occupied)
							CAnnouncementsPanel::addAnnouncement("That item is not equipped.");
						else if (!Inventory[subkey.c]->isEquipped())
							CAnnouncementsPanel::addAnnouncement("That item is not equipped.");
						else
						{
							Inventory[subkey.c]->setEquipped(false);
							EquipSlots[Inventory[subkey.c]->getEquipSlot()].Occupied = false;
							EquipSlots[Inventory[subkey.c]->getEquipSlot()].Item = NULL;
							CAnnouncementsPanel::addAnnouncement(std::string("You stop using ")+Inventory[subkey.c]->getName());
						}
					}
					break;
				}
			}
			delete panel;

			RedrawEverything();
		}
		else if (key.c == 'w')
		{
			TCODConsole* panel = new TCODConsole(80, 50);
			while (true)
			{
				panel->clear();
				panel->print(5, 4, "Choose an item to equip/wield.");
				ShowInventory(panel);

				TCOD_key_t subkey = TCODConsole::waitForKeypress(false);
				if (subkey.vk == TCODK_ESCAPE)
					break;
				else
				{
					if (Inventory[subkey.c])
					{
						if (Inventory[subkey.c]->isEquipped())
							CAnnouncementsPanel::addAnnouncement("That item is already equipped.");
						else if (EquipSlots[Inventory[subkey.c]->getEquipSlot()].Occupied)
							CAnnouncementsPanel::addAnnouncement("You are already using something in that slot.");
						else
						{
							Inventory[subkey.c]->setEquipped(true);
							EquipSlots[Inventory[subkey.c]->getEquipSlot()].Occupied = true;
							EquipSlots[Inventory[subkey.c]->getEquipSlot()].Item = Inventory[subkey.c];
							EquipSlots[Inventory[subkey.c]->getEquipSlot()].Key = subkey.c;
							CAnnouncementsPanel::addAnnouncement(std::string("You start using ")+Inventory[subkey.c]->getName());
						}
					}
					break;
				}
			}
			delete panel;

			RedrawEverything();
		}
		else if (key.c == 'd')
		{
			TCODConsole* panel = new TCODConsole(80, 50);
			while (true)
			{
				panel->clear();
				panel->print(5, 4, "Choose an item to drop.");
				ShowInventory(panel);

				TCOD_key_t subkey = TCODConsole::waitForKeypress(false);
				if (subkey.vk == TCODK_ESCAPE)
					break;
				else
				{
					if (Inventory[subkey.c])
					{
						if (Inventory[subkey.c]->isEquipped())
							CAnnouncementsPanel::addAnnouncement("Remove it first.");
						else
						{
							gGame->getDungeon()->getFloor(CurrentFloor)->getTile(Position)->items.push_back(Inventory[subkey.c]);
							CAnnouncementsPanel::addAnnouncement(std::string("You drop ")+Inventory[subkey.c]->getName());
							Inventory.erase(subkey.c);
						}
					}
					break;
				}
			}
			delete panel;

			RedrawEverything();
		}
		else if (key.c == 'i')
		{
			TCODConsole* panel = new TCODConsole(80, 50);
			while (true)
			{
				panel->clear();
				ShowInventory(panel);

				TCOD_key_t subkey = TCODConsole::waitForKeypress(false);
				if (subkey.vk == TCODK_ESCAPE)
					break;
			}
			delete panel;

			RedrawEverything();
		}
		else if (key.c == 'g')
		{
			CDungeonTile* tile = gGame->getDungeon()->getFloor(CurrentFloor)->getTile(Position);
			if (tile->items.size() > 0)
			{
				float currentWeight = 0;
				for (std::map<char, CItem*>::iterator itr = Inventory.begin(); itr != Inventory.end(); ++itr)
					currentWeight += (*itr).second->getWeight();

				if (currentWeight <= MAXWEIGHT-tile->items[0]->getWeight() && EnoughAlphabet())
				{
					std::stringstream stream(std::stringstream::in | std::stringstream::out);
					stream << "You pick up " << tile->items[0]->getName() << ".";
					CAnnouncementsPanel::addAnnouncement(stream.str());

					Inventory[GetFirstAvailableInvSlot()] = tile->items[0];
					tile->items.pop_back();
					delay = 1;
				}
				else
					CAnnouncementsPanel::addAnnouncement("Your inventory is full.");
			}
			else
				CAnnouncementsPanel::addAnnouncement("There's nothing here to pick up.");

			CAnnouncementsPanel::draw();
			TCODConsole::root->flush();
		}
		else if (key.c == 'l')
		{
			int lx = Position.x(), ly = Position.y();
			bool firstLook = true;
			while(true)
			{
				CAnnouncementsPanel::draw();
				CGamePanel::draw('X', pair(lx, ly), getSight(), gGame->getDungeon()->getFloor(CurrentFloor), true, this);
				TCODConsole::root->flush();

				TCOD_key_t subkey = TCODConsole::waitForKeypress(false);
				if (subkey.vk == TCODK_ESCAPE)
					break;
				else if ((subkey.vk == TCODK_LEFT || subkey.vk == TCODK_KP4) && lx > 0)
					lx--;
				else if ((subkey.vk == TCODK_RIGHT || subkey.vk == TCODK_KP6) && lx < gGame->getDungeon()->getFloor(CurrentFloor)->getSize().x())
					lx++;
				else if ((subkey.vk == TCODK_UP || subkey.vk == TCODK_KP8) && ly > 0)
					ly--;
				else if ((subkey.vk == TCODK_DOWN || subkey.vk == TCODK_KP2) && ly < gGame->getDungeon()->getFloor(CurrentFloor)->getSize().y())
					ly++;
				else if (subkey.vk == TCODK_KP7 && ly > 0 && lx > 0)
				{
					ly--;
					lx--;
				}
				else if (subkey.vk == TCODK_KP9 && ly > 0 && lx < gGame->getDungeon()->getFloor(CurrentFloor)->getSize().x())
				{
					ly--;
					lx++;
				}
				else if (subkey.vk == TCODK_KP1 && ly < gGame->getDungeon()->getFloor(CurrentFloor)->getSize().y() && lx > 0)
				{
					ly++;
					lx--;
				}
				else if (subkey.vk == TCODK_KP3 && ly < gGame->getDungeon()->getFloor(CurrentFloor)->getSize().y() && lx < gGame->getDungeon()->getFloor(CurrentFloor)->getSize().x())
				{
					ly++;
					lx++;
				}

				if (!firstLook)
					CAnnouncementsPanel::Announcements.pop_back();

				CAnnouncementsPanel::addAnnouncement(look(pair(lx, ly)));
				firstLook = false;
			}

			CAnnouncementsPanel::draw();
			CGamePanel::draw(Shape, Position, getSight(), gGame->getDungeon()->getFloor(CurrentFloor));
			TCODConsole::root->flush();
		}
		else if (key.c == 'A')
		{
			TCODConsole* panel = new TCODConsole(80, 50);
			unsigned int page = 0;
			while (true)
			{
				panel->clear();

				unsigned int i = 0;
				for (std::vector<std::string>::reverse_iterator it = CAnnouncementsPanel::Announcements.rbegin()+(page*50); it != CAnnouncementsPanel::Announcements.rend(); ++it)
				{
					if (i <50-1)
					{
						panel->print(3, 50-1-i, (*it).c_str());
						++i;
					}
					else
						break;
				}

				TCODConsole::blit(panel, 0, 0, 80, 50, TCODConsole::root, 0, 0);
				TCODConsole::root->flush();

				TCOD_key_t subkey = TCODConsole::waitForKeypress(false);
				if (subkey.vk == TCODK_ESCAPE)
					break;
				else if (subkey.vk == TCODK_PAGEUP && (page+1)*50 < CAnnouncementsPanel::Announcements.size())
					page++;
				else if (subkey.vk == TCODK_PAGEDOWN && page > 0)
					page--;
			}
			delete panel;

			RedrawEverything();
		}
		else
			delay = 0;

		CTurnSchedule::scheduleTurn(delay, this);
	}
	else
	{
		Quit = true;
		CAnnouncementsPanel::addAnnouncement("You have died...");
		CAnnouncementsPanel::addAnnouncement("Press a key to quit.");
		CAnnouncementsPanel::draw();
		TCODConsole::root->flush();

		TCODConsole::waitForKeypress(true);
	}
}

bool CPlayer::EnoughAlphabet()
{
	if (GetFirstAvailableInvSlot() == '+')
		return false;
	return true;
}

char CPlayer::GetFirstAvailableInvSlot()
{
	char slot = 'a';
	while (Inventory[slot] != NULL)
		slot = CHelpers::getNextLetter(slot);

	return slot;
}

int CPlayer::MoveAttack(pair mod)
{
	int delay = 1;
	if (gGame->getDungeon()->getFloor(CurrentFloor)->getTile(Position + mod)->isPassable())
	{
		CEntity* e = gGame->getDungeon()->getFloor(CurrentFloor)->getEntityAt(Position + mod);
		if (e != NULL)
			delay = attack(e);
		else
		{
			Position += mod;
			delay = getSpeed();
		}
	}
	else
		return 0;

	return delay;
}

void CPlayer::RedrawEverything()
{
	CAnnouncementsPanel::draw();
	CGamePanel::draw(Shape, Position, getSight(), gGame->getDungeon()->getFloor(CurrentFloor));
	CPlayerInfoPanel::draw();
	TCODConsole::root->flush();
}

void CPlayer::ShowInventory(TCODConsole* panel)
{
	if (Inventory.size() > 0)
	{
		float weight = 0;
		unsigned int i = 0;
		for (std::map<char, CItem*>::iterator itr = Inventory.begin(); itr != Inventory.end(); ++itr)
		{
			if ((*itr).second && !(*itr).second->isEquipped())
			{
				std::string invstr("(");
				invstr += (*itr).first;
				invstr += ") ";
				invstr += (*itr).second->getName();
				panel->print(5, 5+i, invstr.c_str());
				weight += (*itr).second->getWeight();
				++i;
			}
		}
		std::stringstream ss;
		ss << "Weight: " << weight << "/" << MAXWEIGHT << "lbs";
		panel->print(5, 5+i, ss.str().c_str());
	}
	else
		panel->print(5, 5, "Your inventory is empty.");

	TCODConsole::blit(panel, 0, 0, 80, 50, TCODConsole::root, 0, 0);
	TCODConsole::root->flush();
}

void CPlayer::ShowWorn(TCODConsole* panel)
{
	for (unsigned int i = 1; i < EES_NUM_SLOTS; ++i)
	{
		if (EquipSlots[(EEquipSlot)i].Item && EquipSlots[(EEquipSlot)i].Occupied == true)
		{
			std::stringstream invstr(std::stringstream::in | std::stringstream::out);
			invstr << CItem::getEquipSlotName(EquipSlots[(EEquipSlot)i].Item->getEquipSlot()) << " | (" << EquipSlots[(EEquipSlot)i].Key << ") " << EquipSlots[(EEquipSlot)i].Item->getName();
			panel->print(5, 5+i, invstr.str().c_str());
		}
		else
		{
			std::stringstream invstr(std::stringstream::in | std::stringstream::out);
			invstr << CItem::getEquipSlotName((EEquipSlot)i) << " | None";
			panel->print(5, 5+i, invstr.str().c_str());
		}
	}

	TCODConsole::blit(panel, 0, 0, 80, 50, TCODConsole::root, 0, 0);
	TCODConsole::root->flush();
}

CPlayer* gPlayer;

